<template>
  <el-form class="form-slot" :inline="inline" :label-width="computed_label_width" @submit.prevent.stop="submit" >
    <slot name="default" />

    <div class="button-list">
      <el-button v-for="row in computed_button_list" :type="row.type" :size="row.size" @click="row.onclick">{{ row.title }}</el-button>
    </div>
    <input v-if="enter_submit" type="submit" style="display: none"/>
  </el-form>
</template>

<script setup lang="ts">
// @ts-ignore
import {string_width_max} from "@/js/dom_tool.js";
import {computed, onMounted, type PropType, provide, type Ref, ref, watch} from "vue";
import {provideFormSlot} from "@/components/provide_keys.ts";
// @ts-ignore
import {decode_btns} from "@/js/s94_tool";


defineOptions({
  _title_: '表单容器',
  _tip_: '搭配FormItem组件使用，提供表单值汇总、校验、错误信息、重置、快捷操作',
  components: {},
})
const emit = defineEmits(['submit', 'reset', 'change'])

const props = defineProps({
  button_list: [Array, Object],
  remove_name: Boolean,
  inline: Boolean,
  enter_submit: Boolean,
  submit_before: {
    type: Function as PropType<(data:{[name:string]: any})=>boolean|Promise<boolean>>,
    default: (val:any)=>true
  },
  submit_after: {
    type: Function as PropType<(data:{[name:string]: any})=>any>,
    default: (val:any)=>true
  },
})

const error_map:Ref<{[name:string]: string}> = ref({})
const item_list:Ref<{
  props: any;
  update_value: (val: any) => Promise<any>;
  validate: () => string;
  value: Ref<any>;
}[]> = ref([])

provide(provideFormSlot, {
  error_map: error_map,
  push_item: (item_row)=>{
    return item_list.value.push(item_row)-1
  },
  delete_item: (index)=>{
    delete item_list.value[index];
  },
})

const computed_button_list = computed(()=>{
  if (!props.button_list){
    return [
      {title:'重置', type:'', onclick:reset},
      {title:'提交', type:'primary', onclick:submit},
    ]
  }else {
    return decode_btns(props.button_list);
  }
})

const computed_label_width = computed(()=>{
  let label_list =  Object.values(item_list.value).filter(row=>{
    const {type, remove} = row.props;
    if (!~["hidden","br"].indexOf(type) && !remove) return true;
  }).map(row=>row.props.label);
  return string_width_max(label_list, '14px')+20+'px';
})

const computed_data = computed(()=>{
  let res:{[name:string]: any} = {}

  Object.values(item_list.value).forEach(row=>{
    const {name} = row.props;
    if (name){
      res[name] = row.value;
    }
  })
  return res;
})

watch(computed_data, (v, oldV)=>{
  emit('change', v)
}, {deep:true})


let reset_data_json = '{}';
function reset(reset_value:{[name:string]: any}){
  error_map.value = {}
  let value_arr = JSON.parse(reset_data_json);
  if (reset_value) {
    Object.assign(value_arr, reset_value);
    reset_data_json = JSON.stringify(value_arr);
  }
  Object.values(item_list.value).forEach(row=>{
    const {name} = row.props;
    if (name && typeof(value_arr[name])!='undefined') {
      row.value = value_arr[name];
    }
  })
  emit('reset', computed_data.value)
}
function set_reset_value(){
  reset_data_json = JSON.stringify(computed_data.value);
}

function set_error_map(data:{[name:string]: string}){
  error_map.value = data;
}
function validate(){
  set_error_map({})
  let error_map:{[name:string]: string} = {}
  let has_error = false;
  Object.values(item_list.value).forEach(row=>{
    const {name} = row.props;
    const err_msg = row.validate();
    if (err_msg) error_map[name] = err_msg;
    has_error = !!(has_error || err_msg);
  })
  if (has_error) {
    let err = new Error('表单验证未通过');
    // @ts-ignore
    err.error_map = error_map;
    throw err;
  }
  return true;
}
async function submit(){
  validate();
  await submit_force();
}

/**
 * 跳过验证，强行提交
 */
async function submit_force(){
  let data = computed_data.value;
  let res = await props.submit_before(data);
  if (res === false) return console.log('【FormSlot】submit_before 回调方法返回 false, 中止了 submit 事件')
  emit('submit', data);
  await props.submit_after(data);
}

onMounted(()=>{
  set_reset_value()
})


defineExpose({
  computed_data,
  set_error_map,
  reset,
  set_reset_value,
  validate,
  submit,
  submit_force,
})

</script>

<style scoped>
.form-slot{position: relative;overflow: hidden auto;width: 100%;max-height: 100%;background: var(--color-bg);border-radius: 8px;}
.button-list{width: 100%;display: flex;justify-content: flex-end;position: sticky;bottom: 0;z-index: 9;background: var(--color-bg);padding: 10px;}
</style>